/*
 * Copyright (C) 2011-2021 Intel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
 /**
  * File: id_enclave.cpp
  *
  * Description: Get QE_ID for TD-based quoting
  *
  */

#include <sgx_secure_align.h>
#include <string.h>
#include <sgx_utils.h>
#include <sgx_tcrypto.h>
#include <sgx_quote_3.h>
#include "user_types.h"
#include "id_enclave_t.c"


#define REF_N_SIZE_IN_BYTES    384
#define REF_E_SIZE_IN_BYTES    4
static const uint8_t g_ref_pubkey_e_be[REF_E_SIZE_IN_BYTES] = { 0x00,0x01,0x00,0x01 };
static const uint8_t g_ref_pubkey_n_be[REF_N_SIZE_IN_BYTES] =
{ 0xd3,0x96,0xf9,0x43,0x43,0x11,0x00,0x1c,0x69,0x44,0x9c,0x3b,0xfd,0xee,0x8f,0x38,
0xcd,0x95,0xcd,0xad,0x74,0x09,0x7c,0x87,0xf1,0xa7,0x65,0x02,0x4c,0x87,0xc1,0x57,
0x30,0xa5,0xc9,0xa6,0xa4,0xcc,0xf9,0x1d,0x62,0x18,0x1e,0x00,0xa6,0x74,0x27,0x58,
0x59,0xca,0x1b,0x1d,0xf5,0x31,0x0e,0xf2,0xd5,0xe1,0x79,0x37,0x39,0x94,0x3d,0x3d,
0xe2,0x50,0x93,0x12,0xd6,0x03,0xe5,0x19,0x3a,0x48,0xf0,0xae,0x0c,0x37,0xee,0xe0,
0x57,0x27,0xbd,0xec,0x17,0x1b,0x0f,0x39,0x86,0x06,0x54,0x20,0x74,0x84,0x34,0xbe,
0x34,0xfa,0x71,0x6f,0xa1,0xf5,0x4c,0x9a,0x52,0x0f,0xc4,0xbc,0x2d,0x7a,0x2e,0x17,
0xe3,0x5d,0xa2,0x0e,0xca,0x39,0x07,0x98,0xa9,0x05,0x1a,0x34,0xfb,0x8f,0x60,0x9c,
0x3a,0x1e,0x26,0x30,0x0b,0xf3,0xf3,0x49,0x40,0xd9,0xf7,0x5d,0xcb,0xd1,0xbf,0x57,
0x8d,0xe5,0x2d,0xce,0x98,0x57,0x35,0xf1,0x93,0xc3,0x19,0x2e,0x80,0x55,0x37,0xab,
0x8d,0x64,0x08,0xda,0xe6,0xdd,0x64,0xb4,0x62,0x83,0x8d,0x43,0xaa,0xd2,0x7b,0xc2,
0x63,0xaa,0x97,0xde,0xed,0x09,0x92,0xd6,0x88,0x56,0x86,0xcd,0x08,0x23,0x03,0x27,
0x9a,0x78,0x7c,0xf4,0x36,0x12,0xf5,0xb1,0xe6,0x1d,0x54,0xab,0x88,0x69,0xff,0x18,
0x4f,0xdc,0x87,0xee,0x34,0xa6,0x68,0xb1,0x81,0x67,0xb6,0xce,0x0a,0x70,0x14,0xbc,
0xb3,0xe1,0x8d,0x76,0x1c,0x73,0xde,0x00,0xab,0x41,0xca,0x40,0x51,0x53,0x63,0x04,
0xc3,0x63,0x0b,0xca,0x62,0xda,0xaa,0x9c,0xe5,0x01,0xb7,0xc0,0x0f,0x7e,0x0b,0xb0,
0xbe,0xe9,0xf8,0x0d,0xb3,0xb6,0x64,0xfd,0xcd,0x95,0x17,0x9c,0x57,0x8e,0xec,0xc4,
0xac,0x8b,0x36,0x01,0x5e,0x4c,0x6d,0x1e,0x21,0x49,0xa0,0x1d,0xde,0x04,0x39,0x6b,
0x34,0x68,0x44,0xea,0x06,0x76,0xe0,0x8d,0x1f,0xa2,0xc0,0x26,0x05,0xcc,0x91,0xbe,
0xa3,0x17,0xc8,0x75,0x46,0x85,0x10,0x39,0x16,0x50,0x8e,0x02,0x43,0x98,0x31,0x70,
0x69,0xd8,0x34,0x71,0x82,0xe7,0x48,0x26,0xcd,0xc1,0x82,0xd3,0xeb,0x6f,0xe9,0x58,
0xe7,0x06,0x77,0x10,0x1f,0xdf,0x49,0x76,0x30,0xa7,0x68,0x42,0xb0,0x16,0xd7,0xda,
0x92,0x75,0xd5,0x7f,0x2e,0x75,0x43,0xac,0x83,0xb0,0x1f,0xc3,0x90,0x19,0xce,0xaa,
0x94,0xd0,0x2e,0x5a,0x6c,0x13,0x72,0xe7,0xa6,0xb5,0xc0,0x45,0x81,0xe3,0x53,0x27 };

/** Structure definition of the RSA key used to decrypt the PCE's PPID */
typedef struct _pce_rsaoaep_3072_encrypt_pub_key_t {
    uint8_t n[REF_RSA_OAEP_3072_MOD_SIZE];   ///< RSA 3072 public modulus
    uint8_t e[REF_RSA_OAEP_3072_EXP_SIZE];   ///< RSA 3072 public exponent
} pce_rsaoaep_3072_encrypt_pub_key_t;

static const char QE_ID_STRING[] = "QE_ID_DER";

/**
 * The QE_ID is a platform ID that is not associated with a particular SVN but is dependent on the Quoting Enclave's
 * (QE) MRSIGNER and its Seal Key.  The QE_ID is designed to be dependent on the seal key which is dependent on the
 * platform's OWNER_EPOCH value.  The OWNER_EPOCH value is set by the platform owner in the BIOS configuration.  If the
 * BIOS's non-volatile memory (FLASH) is wiped, then the QE_ID will change even if generated by the same QE.  This
 * prevents the QE_ID from being a true HW ID which cannot be modified by the platform owner.
 *
 * 1) QE_ID-Seed = EGETKEY(KEYNAME=SEAL_KEY,
 *                         KEY_POLICY=MRSIGNER,
 *                         KEY_ID = 0,
 *                         CPUSVN=0,
 *                         ISVSVN = 0)
 * 2) QE_ID = AES128-CMAC(QE_ID-Seed, 16 bytes below)
 *
 *    Byte Position  |  Value
 *        0          |  0x00
 *       1-9         |  "QE_ID_DER" (ascii encoded)
 *       10-13       |  0x00000000
 *       14-15       |  0x0080 (Big Endian)
 *
 * @param p_id[Out] Pointer to the ID.  Must not be NULL.
 *
 * @return SGX_SUCCESS Successfully created the ID.
 * @return SGX_ERROR_INVALID_PARAMETER The ID pointer is NULL.
 * @return SGX_ERROR_UNEXPECTED Error in the crypto library functions ues to generate the key.
 * @return SGX_ERROR_OUT_OF_MEMORY Heap memory was exhausted.
 *
 */
sgx_status_t ide_get_id(sgx_key_128bit_t *p_id)
{
    sgx_status_t sgx_status = SGX_SUCCESS;
    sgx_key_128bit_t key_tmp;
    sgx_key_request_t qe_id_key_req;

    if (p_id == NULL || !sgx_is_within_enclave(p_id, sizeof(*p_id))) {
        return SGX_ERROR_INVALID_PARAMETER;
    }

    memset(&key_tmp, 0, sizeof(key_tmp));

    // Set up the key request structure for Seal Key with both CPUSVN and ISVSVN set to 0 and KeyID set to 0
    memset(&qe_id_key_req, 0, sizeof(sgx_key_request_t));
    qe_id_key_req.key_name = SGX_KEYSELECT_SEAL; // Seal key
    qe_id_key_req.key_policy = SGX_KEYPOLICY_MRSIGNER;
    qe_id_key_req.attribute_mask.xfrm = 0;
    qe_id_key_req.misc_mask = 0xFFFFFFFF;
    qe_id_key_req.attribute_mask.flags = ~SGX_FLAGS_MODE64BIT; //set all bits except the SGX_FLAGS_MODE64BIT
    sgx_status = sgx_get_key(&qe_id_key_req, &key_tmp);
    if (SGX_SUCCESS != sgx_status) {
        if (SGX_ERROR_OUT_OF_MEMORY != sgx_status) {
        sgx_status = SGX_ERROR_UNEXPECTED;
        }
        goto ret_point;
    }

    uint8_t content[16];
    memset(&content, 0, sizeof(content));
    //1-10bytes: "QE_ID_DER"(ascii encoded)
    memcpy(content + 1, QE_ID_STRING, 9);
    //14-15bytes: 0x0080 (Big Endian)
    content[14] = 0x00;
    content[15] = 0x80;

    // Generate the mac as QE_ID
    ref_static_assert(sizeof(sgx_cmac_128bit_key_t) == sizeof(sgx_key_128bit_t));
    ref_static_assert(sizeof(sgx_cmac_128bit_tag_t) == sizeof(*p_id));
    sgx_status = sgx_rijndael128_cmac_msg(reinterpret_cast<const sgx_cmac_128bit_key_t*>(&key_tmp),
        content,
        sizeof(content),
        reinterpret_cast<sgx_cmac_128bit_tag_t*>(p_id));
    if (SGX_SUCCESS != sgx_status && SGX_ERROR_OUT_OF_MEMORY != sgx_status) {
        sgx_status = SGX_ERROR_UNEXPECTED;
    }
ret_point:
    (void)memset_s(&key_tmp, sizeof(key_tmp), 0, sizeof(key_tmp)); //clear key in stack
    return sgx_status;
}

/**
 * External function exposed through the EDL used to return the QE report and the PPID encryption key required to get
 * the PCE identity information.  The PCE requires that the PPID be encrypted with a public key.  The reference supports
 * 1 type of certification data:
 *      1.  PPID_RSA3072_ENCRYPTED.
 * For PPID_RSA3072_ENCRYPTED, the QE will use the hardcoded public key owned by the quote verifier.
 *
 * @param p_pce_target_info
 *                 [In] Pointer to the target_info buffer of the PCE. It must not be NULL and the full target info
 *                 buffer must reside in the enclave's memory space.
 * @param p_ide_report
 *                 [Out] Pointer to the QE report buffer targeting the PCE. It must not be NULL and full report
 *                 buffer must reside in the enclave's memory space.
 * @param crypto_suite
 *                 [In] Indicates the crypto algorithm to use to encrypt the PPID. Currently, only RSA3072 keys are
 *                 supported.  This is the type of key this function will generate.
 * @param cert_key_type
 *                 [In] Indicates whether to use the hard-coded public key or generate a new one.  This option allows
 *                  the reference to demonstrate creating an encryption key on-demand or to use the hard-coded value.
 *                  Using the hard-coded value typically means the PPID is to remain private on the platform. Must be
 *                  PPID_RSA3072_ENCRYPTED.
 *
 * @param key_size [In] The size in bytes of the supplied p_public_key buffer.  Currently, it must be equal to the size
 *                 of an RSA3072 public key. 4 bytes 'e' and 384 bytes 'n'.
 * @param p_public_key
 *                 [In, Out] Pointer to the buffer that will contain the public key used to encrypt the PPID. It must
 *                 not be NULL and the buffer must reside within the enclave's memory space.
 *
 * @return SGX_SUCCESS Function successfully generated or retrieved the encryption key and generated a REPORT
 *         targeting the PCE.
 * @return SGX_ERROR_INVALID_PARAMETER Invalid parameter.
 * @return SGX_ERROR_UNEXPECTED An internal error occurred.
 */
sgx_status_t ide_get_pce_encrypt_key(
    const sgx_target_info_t* p_pce_target_info,
    sgx_report_t* p_ide_report,
    uint8_t crypto_suite,
    uint16_t cert_key_type,
    uint32_t key_size,
    uint8_t* p_public_key)
{
    sgx_status_t sgx_status = SGX_SUCCESS;
    sgx_report_data_t report_data = { 0 };
    sgx_sha_state_handle_t sha_handle = NULL;
    pce_rsaoaep_3072_encrypt_pub_key_t* p_rsa_pub_key;

    if (p_pce_target_info == NULL || !sgx_is_within_enclave(p_pce_target_info, sizeof(*p_pce_target_info))) {
        return SGX_ERROR_INVALID_PARAMETER;
    }

    if (p_public_key == NULL || !sgx_is_within_enclave(p_public_key, key_size)) {
        return SGX_ERROR_INVALID_PARAMETER;
    }

    if (p_ide_report == NULL || !sgx_is_within_enclave(p_ide_report, sizeof(*p_ide_report))) {
        return SGX_ERROR_INVALID_PARAMETER;
    }

    if (crypto_suite != PCE_ALG_RSA_OAEP_3072) {
        return SGX_ERROR_INVALID_PARAMETER;
    }

    if (key_size != sizeof(*p_rsa_pub_key)) {
        return SGX_ERROR_INVALID_PARAMETER;
    }
    // Only PPID_RSA3072_ENCRYPTED is supported when using production mode PCE.
    if (PPID_RSA3072_ENCRYPTED != cert_key_type) {
        return SGX_ERROR_INVALID_PARAMETER;
    }

    if ((p_pce_target_info->attributes.flags & SGX_FLAGS_PROVISION_KEY) != SGX_FLAGS_PROVISION_KEY ||
        (p_pce_target_info->attributes.flags & SGX_FLAGS_DEBUG) != 0)
    {
        //PCE must have access to provisioning key
        //Can't be debug PCE
        return(SGX_ERROR_INVALID_PARAMETER);
    }

    p_rsa_pub_key = (pce_rsaoaep_3072_encrypt_pub_key_t*)p_public_key;
    memcpy(p_rsa_pub_key->e, g_ref_pubkey_e_be, sizeof(p_rsa_pub_key->e));
    memcpy(p_rsa_pub_key->n, g_ref_pubkey_n_be, sizeof(p_rsa_pub_key->n));

    // report_data = SHA256(crypto_suite||rsa_pub_key)||0-padding
    do {
        sgx_status = sgx_sha256_init(&sha_handle);
        if (SGX_SUCCESS != sgx_status)
            break;

        sgx_status = sgx_sha256_update(&crypto_suite,
            sizeof(uint8_t),
            sha_handle);
        if (SGX_SUCCESS != sgx_status)
            break;
        //(MOD followed by e)
        sgx_status = sgx_sha256_update(p_rsa_pub_key->n,
            sizeof(p_rsa_pub_key->n),
            sha_handle);
        if (SGX_SUCCESS != sgx_status)
            break;
        sgx_status = sgx_sha256_update(p_rsa_pub_key->e,
            sizeof(p_rsa_pub_key->e),
            sha_handle);
        if (SGX_SUCCESS != sgx_status)
            break;
        sgx_status = sgx_sha256_get_hash(sha_handle,
            reinterpret_cast<sgx_sha256_hash_t *>(&report_data));
    } while (0);
    if (SGX_SUCCESS != sgx_status) {
        if (SGX_ERROR_OUT_OF_MEMORY != sgx_status)
            sgx_status = SGX_ERROR_UNEXPECTED;
        goto ret_point;
    }

    sgx_status = sgx_create_report(p_pce_target_info, &report_data, p_ide_report);
    if (SGX_SUCCESS != sgx_status && SGX_ERROR_OUT_OF_MEMORY != sgx_status) {
        sgx_status = SGX_ERROR_UNEXPECTED;
    }

ret_point:
    // Clear critical output data on error
    if (SGX_SUCCESS != sgx_status) {
        memset_s(p_ide_report, sizeof(*p_ide_report), 0, sizeof(*p_ide_report));
    }
    if (sha_handle != NULL) {
        sgx_sha256_close(sha_handle);
    }

    return sgx_status;
}
